package io.renren.modules.xsl.utils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import java.util.regex.Pattern;

public class AesUtil {

    public static final String PREFIX_AES = "A-";
    private static final String AES_ALGORITHM = "AES/CBC/PKCS5PADDING";
    private static final int AES_KEY_LENGTH = 32;

    public static byte[] encrypt(String algorithm, byte[] key, byte[] iv, byte[] bytes) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            AlgorithmParameterSpec ivParameterSpec = null;
            if (iv != null) {
                ivParameterSpec = new IvParameterSpec(iv);
            }

            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(bytes);
        } catch (Exception e) {
            throw new RuntimeException("encrypt failed", e);
        }
    }

    public static byte[] decrypt(String algorithm, byte[] key, byte[] iv, byte[] bytes) {
        try {
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
            AlgorithmParameterSpec ivParameterSpec = null;
            if (iv != null) {
                ivParameterSpec = new IvParameterSpec(iv);
            }

            Cipher cipher = Cipher.getInstance(algorithm);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(bytes);
        } catch (Exception e) {
            throw new RuntimeException("decrypt failed", e);
        }
    }

    public static String encryptHex(String algorithm, byte[] key, byte[] iv, String data, Charset charset) {
        return HexUtil.parseByte2Hex(encrypt(algorithm, key, iv, data.getBytes(charset)));
    }

    public static String encryptHex(String algorithm, byte[] key, byte[] iv, String data) {
        return encryptHex(algorithm, key, iv, data, StandardCharsets.UTF_8);
    }

    public static String decrypt(String algorithm, byte[] key, byte[] iv, String encrypted, Charset charset) {
        byte[] data;
        if (HexUtil.isHex(encrypted)) {
            data = HexUtil.parseHex2Byte(encrypted);
        } else {
            data = Base64.getDecoder().decode(encrypted);
        }
        byte[] decrypt = decrypt(algorithm, key, iv, data);
        return new String(decrypt, charset);
    }

    public static String decrypt(String algorithm, byte[] key, byte[] iv, String encrypted) {
        return decrypt(algorithm, key, iv, encrypted, StandardCharsets.UTF_8);
    }

    public static String encode(String data, String password) {
        password = fillPassword(password);
        // 加密成Hex
        return PREFIX_AES + AesUtil.encryptHex(AES_ALGORITHM,
                password.getBytes(), password.substring(0, 16).getBytes(), data);

    }

    public static String decode(String data, String password) {
        password = fillPassword(password);
        return AesUtil.decrypt(AES_ALGORITHM,
                password.getBytes(), password.substring(0, 16).getBytes(), data);
    }

    private static String fillPassword(String password) {
        if (password == null || password.length() == 0) {
            throw new RuntimeException("加密秘钥不合法");
        }
        int length = password.length();
        if (length < AES_KEY_LENGTH) {
            return password + repeat('$', AES_KEY_LENGTH - password.length());
        } else if (length > AES_KEY_LENGTH) {
            return password.substring(0, AES_KEY_LENGTH);
        } else {
            return password;
        }
    }

    private static String repeat(char ch, int count) {
        StringBuilder sb = new StringBuilder(count);
        for (int i = 0; i < count; i++) {
            sb.append(ch);
        }
        return sb.toString();
    }


    public static class HexUtil {

        public static final Pattern HEX = Pattern.compile("^[a-fA-F0-9]+$");

        public static boolean isHex(CharSequence content) {
            if (content != null) {
                return HEX.matcher(content).matches();
            }
            return false;
        }

        /**
         * 将二进制转换成16进制
         */
        public static String parseByte2Hex(byte[] buf) {
            StringBuilder sb = new StringBuilder();
            for (byte b : buf) {
                String hex = Integer.toHexString(b & 0xFF);
                if (hex.length() == 1) {
                    hex = '0' + hex;
                }
                sb.append(hex.toUpperCase());
            }
            return sb.toString();
        }

        /**
         * 将16进制转换为二进制
         */
        public static byte[] parseHex2Byte(String hexStr) {
            int hexLen = hexStr.length();
            if (hexLen < 1) {
                return null;
            }
            byte[] result;
            if (hexLen % 2 == 1) {
                //奇数
                hexLen++;
                result = new byte[(hexLen / 2)];
                hexStr = "0" + hexStr;
            } else {
                //偶数
                result = new byte[(hexLen / 2)];
            }
            int j = 0;
            for (int i = 0; i < hexLen; i += 2) {
                int high = Integer.parseInt(hexStr.substring(i, i + 2), 16);
                result[j] = (byte) high;
                j++;
            }
            return result;
        }
    }
}